﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Mime;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using Zhp.Auth.IService;
using Zhp.Common.Appsetting;
using Zhp.Common.Helper;
using Zhp.Common.WebApp;
using Zhp.Model.Common;

namespace Zhp.Auth
{
    public class RefreshTokenAuthenticationHandler : AuthenticationHandler<RefreshTokenOptions>
    {
        IOnlineService _onlineSvc;
        public RefreshTokenAuthenticationHandler(IOptionsMonitor<RefreshTokenOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            IOnlineService onlineSvc,
            ICurrentUser currentUser) : base(options, logger, encoder)
        {
            _onlineSvc = onlineSvc;
        }

        /// <summary>
        /// 资源文件校验
        /// </summary>
        /// <returns></returns>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            string token = Context.Request.Headers[AuthHeaderTypeConst.Authorization].ToString();
            token = token.Replace(JwtBearerDefaults.AuthenticationScheme + " ", "");
            if (!string.IsNullOrEmpty(token))
            {
                var jwtHandler = new JwtSecurityTokenHandler();
                var jwtOptions = AppSettingHelper.GetOptions<JwtOptions>();
                var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SigningKey));
                // 创建一个TokenValidationParameters对象，用于配置JWT验证的参数
                var tokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingKey,
                    ValidateLifetime = true,
                    ValidateIssuer = true,
                    ValidIssuer = jwtOptions.Issuer, //发行人
                    ValidateAudience = true,
                    ValidAudience = jwtOptions.Audience, //订阅人
                    ClockSkew = TimeSpan.FromSeconds(0),//时间偏移量，过期了还能用
                    RequireExpirationTime = true
                };
                // 验证JWT并获取ClaimsPrincipal对象
                //ClaimsPrincipal principal = jwtHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);
                var vilidate = await jwtHandler.ValidateTokenAsync(token, tokenValidationParameters);
                if (vilidate.IsValid)
                {
                    var refreshTokenId = vilidate.ClaimsIdentity.Claims.First(x => x.Type == AuthClaimTypeConst.RefreshToken).Value;
                    var cacheId = vilidate.ClaimsIdentity.Claims.First(x => x.Type == AuthClaimTypeConst.CacheId).Value;
                    var isOnline = _onlineSvc.IsRefreshOnline(cacheId, refreshTokenId);
                    if (!isOnline.IsOnline)
                    {
                        return await Task.FromResult(AuthenticateResult.Fail(new SecurityTokenInvalidLifetimeException(isOnline.Message)));
                    }
                    else
                    {
                        var user = isOnline.CurrentUserInfo;
                        var claims = new Claim[]
                        {
                            new Claim(AuthClaimTypeConst.CompanyCode, user.CompanyCode.ToString()),
                            new Claim(AuthClaimTypeConst.OnlineId, user.OnlineId.ToString()),
                            new Claim(AuthClaimTypeConst.TokenType, user.TokenType.ToString()),
                            new Claim(AuthClaimTypeConst.CacheId, user.CacheId),
                            new Claim(AuthClaimTypeConst.UserId, user.UserId.ToString()),
                            //new Claim(AuthClaimTypeConst.WorkDayId, user.WorkDayId),
                            new Claim(AuthClaimTypeConst.UserCode, user.UserCode),
                            new Claim(AuthClaimTypeConst.UserName, user.UserName),
                            new Claim(AuthClaimTypeConst.RoleIds, user.RoleIds.ToJson()),
                            new Claim(AuthClaimTypeConst.RefreshToken, user.RefreshTokenId.ToString()),
                            new Claim(AuthClaimTypeConst.AccessTokenId, user.AccessTokenId.ToString())//jwt编号
                        };
                        var identity = new ClaimsIdentity(claims, RefreshTokenDefaults.AuthenticationScheme);
                        var principal = new ClaimsPrincipal(identity);
                        var ticket = new AuthenticationTicket(principal, RefreshTokenDefaults.AuthenticationScheme);
                        return await Task.FromResult(AuthenticateResult.Success(ticket));
                    }
                }
                else
                {
                    return await Task.FromResult(AuthenticateResult.Fail("ticket"));
                }
            }
            else
            {
                //获取token并且解析出token，判断在在线用户中是否存在
                return await Task.FromResult(AuthenticateResult.Fail("登录过期，请重新登录！"));
            }
        }

        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            var result = new ApiResult
            {
                Status = StatusCodeConst.AuthRefreshTokenStatusCode,
                Message = StatusCodeConst.AuthRefreshTokenMessage,
                Url = Context.Request.Path
            };
            Context.Response.Headers.TryAdd(AuthHeaderTypeConst.TokenStatus, StatusCodeConst.AuthRefreshTokenStatusCode.ToString());
            Context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            Context.Response.ContentType = MediaTypeNames.Application.Json;
            await Context.Response.WriteAsync(result.ToJson(true));
        }

        protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
        {
            var result = new ApiResult
            {
                Status = StatusCodeConst.AuthForbiddenStatusCode,
                Message = StatusCodeConst.AuthForbiddenMessage,
                Url = Context.Request.Path
            };
            Context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            Context.Response.ContentType = MediaTypeNames.Application.Json;
            await Context.Response.WriteAsync(result.ToJson(true));
        }
    }

    public class RefreshTokenDefaults
    {
        public const string AuthenticationScheme = "RefreshTokenScheme";
        //public const string AuthenticationDisplayScheme = "RefreshTokenDisplayScheme";
    }

    public class RefreshTokenOptions : AuthenticationSchemeOptions
    {
        public string TokenHeaderName { get; set; }
    }
}
